/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.skos.core;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import org.apache.log4j.Logger;
import org.openrdf.model.Namespace;
import org.openrdf.model.URI;
import org.openrdf.model.impl.NamespaceImpl;

import be.destin.rdf.changes.Power;
import be.destin.rdf.changes.Status;
import be.destin.skos.html.LocalError;
import be.destin.skos.html.SkosHtmlManager;
import be.destin.skos.search.LabelMatchType;
import be.destin.skos.search.SearchResult;

/**
 * @author Christophe Dupriez
 * 
 * Instance of an unloadable ConceptScheme so we do not try to reload it all the time...
 * Scheme metadata is accepted...
 * This scheme is the ancestor of all others so metadata management is not rewritten all the time.
 * 
 */
/*
It is desirable to have complete compliance of Schemes with Dublin Core (unqualified at least)
From http://dublincore.org/documents/dcmi-type-vocabulary/

DONE: http://purl.org/dc/elements/1.1/contributor 
An entity responsible for making contributions to the resource.
Examples of a Contributor include a person, an organization, or a service. Typically, the name of a Contributor should be used to indicate the entity.
 
http://purl.org/dc/elements/1.1/coverage 
The spatial or temporal topic of the resource, the spatial applicability of the resource, or the jurisdiction under which the resource is relevant.
Spatial topic and spatial applicability may be a named place or a location specified by its geographic coordinates. Temporal topic may be a named period, date, or date range. A jurisdiction may be a named administrative entity or a geographic place to which the resource applies. Recommended best practice is to use a controlled vocabulary such as the Thesaurus of Geographic Names [TGN]. Where appropriate, named places or time periods can be used in preference to numeric identifiers such as sets of coordinates or date ranges.

DONE: http://purl.org/dc/elements/1.1/creator 
An entity primarily responsible for making the resource.
Examples of a Creator include a person, an organization, or a service. Typically, the name of a Creator should be used to indicate the entity.

http://purl.org/dc/elements/1.1/date 
A point or period of time associated with an event in the lifecycle of the resource.
Date may be used to express temporal information at any level of granularity. Recommended best practice is to use an encoding scheme, such as the W3CDTF profile of ISO 8601 [W3CDTF].
 
DONE (multilingual): http://purl.org/dc/elements/1.1/description 
An account of the resource.
Description may include but is not limited to: an abstract, a table of contents, a graphical representation, or a free-text account of the resource.
 
http://purl.org/dc/elements/1.1/format 
The file format, physical medium, or dimensions of the resource.
Examples of dimensions include size and duration. Recommended best practice is to use a controlled vocabulary such as the list of Internet Media Types [MIME].
 
http://purl.org/dc/elements/1.1/identifier 
An unambiguous reference to the resource within a given context.
Recommended best practice is to identify the resource by means of a string conforming to a formal identification system. 

http://purl.org/dc/elements/1.1/language 
A language of the resource.
Recommended best practice is to use a controlled vocabulary such as RFC 4646 [RFC4646].
 
http://purl.org/dc/elements/1.1/publisher 
An entity responsible for making the resource available.
Examples of a Publisher include a person, an organization, or a service. Typically, the name of a Publisher should be used to indicate the entity.

http://purl.org/dc/elements/1.1/relation 
A related resource.
Recommended best practice is to identify the related resource by means of a string conforming to a formal identification system. 

http://purl.org/dc/elements/1.1/rights 
Information about rights held in and over the resource.
Typically, rights information includes a statement about various property rights associated with the resource, including intellectual property rights.
 
http://purl.org/dc/elements/1.1/source 
A related resource from which the described resource is derived.
The described resource may be derived from the related resource in whole or in part. Recommended best practice is to identify the related resource by means of a string conforming to a formal identification system.
 
http://purl.org/dc/elements/1.1/subject 
The topic of the resource.
Typically, the subject will be represented using keywords, key phrases, or classification codes. Recommended best practice is to use a controlled vocabulary. To describe the spatial or temporal topic of the resource, use the Coverage element.
 
DONE: http://purl.org/dc/elements/1.1/title 
A name given to the resource.
Typically, a Title will be a name by which the resource is formally known.
 
http://purl.org/dc/elements/1.1/type 
The nature or genre of the resource.
Recommended best practice is to use a controlled vocabulary such as the DCMI Type Vocabulary [DCMITYPE]. To describe the file format, physical medium, or dimensions of the resource, use the Format element.
 */
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(propOrder={"title","description","rights","creator","contributor","namespace","uri","schemaOrg","supraLanguage","help","icon","display","create","internalNote"})
public class NoScheme implements SchemeInterface, Comparable<SchemeInterface>, URI {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public static final String schemaOrgBase = "http://schema.org/"; // MUST BE IN LOWERCASE OR CODE MUST BE ADAPTED!!!

	/** log4j category */
	private static Logger log = Logger.getLogger(NoScheme.class);

	protected transient SkosManager managerInstance = null;
	protected Concept conceptRepresentation = null;
	
//	protected String code;
	protected String application;
	protected LinkedList<String> creators;
	protected LinkedList<String> contributors;
	protected UrlList helps = new UrlList();
    protected UrlList creates = new UrlList();

    protected String uri;
    protected String namespace;
    protected String schemaOrg;
	
    protected boolean hierarchized = false;

	private Date lastLoaded = new Date(); // Initialize itself to it creation date

	protected LinkedList<String> supraLanguage;
	
	private long loadTime = 0;
	private long memorySpace = 0;

	private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	boolean noConcepts = false; // temporarily suspend the availability of member concepts
	
	public NoScheme (){
		super();
		conceptRepresentation = new Concept();
	}

	/**
	 * 
	 */
	public NoScheme(SkosManager managerInstance, String code) {
		conceptRepresentation = new Concept(managerInstance.getMetaScheme());
		setAbout(code);
		setManagerInstance(managerInstance);
	}

	/**
	 * @return the concepts for an handle (redirected or not). Null if none
	 * The entry code can be a "magic" id
	 */
		public synchronized Concept findAbout_Concept(String handle) {
			if (handle == null || handle.length() == 0) return null;
			String entryCode = handle;
			String code = this.getAbout();
			if (entryCode.startsWith(code+SchemeInterface.separator))
				entryCode = entryCode.substring(code.length()+1);
			return getConcept(entryCode);
		}

	/**
	 * @return the code
	 */
	@XmlAttribute(required = true)
	   @XmlID
	public String getAbout() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getAbout();
	}

	public String getAbout_About(String entryCode) {
		String prefix = this.getAbout()+SchemeInterface.separator;
		if (entryCode.startsWith(prefix)) return entryCode;
		return prefix + entryCode;
	}

	/**
	 * @return the complete prefixed code of an entry (after applying
	 *         redirections)
	 */
	public String getAbout_Concept(String fullHandle) {
		return null;
	}

	/**
	 * @return the concepts
	 */
	@XmlTransient
	public synchronized LinkedList<Concept> getConcept() {
		return null;
	}

	@XmlTransient
	public synchronized LinkedList<Concept> getAllConcepts() {
		return null;
	}

	@XmlTransient
	public synchronized LinkedList<ConceptIdentifier> getHasTopConcept() {
		return null;
	}
	/**
	 * @return the concepts currently in Memory
	 */
	@XmlTransient
	public LinkedList<Concept> getConceptInMemory() {
		return getAllConcepts();
	}

	/**
	 * @return the concepts for an handle (redirected or not). Null if none
	 */
	public synchronized LinkedList<Concept> getConcept(Collection<String> fullHandles) {
		return null;
	}

	/**
	 * @return the concepts for an handle (redirected or not). Null if none
	 */
	public synchronized Concept getConcept(String fullHandle) {
		return null;
	}

	/**
	 * @return the displays
	 */
	@XmlElement
	public UrlList getDisplay() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getUrl();
	}

	/**
	 * @return the creation URLs
	 */
	@XmlElement
	public UrlList getCreate() {
		return creates;
	}

    /**
     * @return the internalNote
     */
    @XmlElement
    public String getInternalNote() {
		if (conceptRepresentation == null) return null;
        return conceptRepresentation.getInternalNote();
    }

	/**
	 * @return the helps
	 */
	@XmlElement
	public UrlList getHelp() {
		return helps;
	}

	/**
	 * @return the icons
	 */
	@XmlElement
	public UrlList getIcon() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getIcon();
	}

	/**
	 * @return the titles
	 */
	@XmlElement
	public TermList getTitle() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getPrefLabel();
	}

	/**
	 * @return the descriptions
	 */
	@XmlElement
	public TermList getDescription() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getScopeNote();
	}

	@XmlElement
	public TermList getRights() {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getEditorialNote();
	}

	/**
	 * @return the namespace
	 */
	@XmlElement(name="namespace")
	public String getNamespace() {
		return namespace;
	}

	/**
	 * @return the namespace
	 */
	@XmlElement(name="uri")
	public String getUri() {
		return uri;
	}

	/**
	 * @return the schemaOrg
	 */
	@XmlElement(name="schemaOrg")
	public String getSchemaOrg() {
		return schemaOrg;
	}

	public String toUriSchemaOrg () {
		if (schemaOrg == null || schemaOrg.isEmpty()) return null;
		return schemaOrgBase+schemaOrg;
	}

	/**
	 * @param schemaOrg the schemaOrg to set
	 */
	public void setSchemaOrg(String schemaOrg) {
		this.schemaOrg = schemaOrg;
		if (schemaOrg != null && schemaOrg.toLowerCase().startsWith(schemaOrgBase)) {
			this.schemaOrg = schemaOrg.substring(schemaOrgBase.length());
		}
	}

	public String toUriNamespace () {
		if (namespace != null && !namespace.isEmpty()) return namespace;
		if (uri != null && !uri.isEmpty()) {
			char endC = uri.charAt(uri.length()-1);
			if (endC == '#' || endC == '/' || endC == '_' || endC == '.' || endC == ':') return uri;
			return uri+'#';
		}
		return null;
	}

	public String toUriScheme () {
		if (uri != null && !uri.isEmpty()) return uri;
		if (namespace != null && !namespace.isEmpty()) {
			char endC = namespace.charAt(namespace.length()-1);
			if (endC == '#' || endC == '/' || endC == '_' || endC == '.' || endC == ':') return namespace.substring(0,namespace.length()-1);
		}
		return null;
	}

	public String toUriNamespace (SkosHtmlManager skosHtmlManager) {
		String result = toUriNamespace();
		if (result != null) return result;
		return skosHtmlManager.getApplicationRoot(SkosHtmlManager.askosiDisplayApplication) + "/skos.jsp?about=" + this.getAbout() + SchemeInterface.separator;
	}

	public String toUriScheme (SkosHtmlManager skosHtmlManager) {
		String result = toUriScheme();
		if (result != null) return result;
		return  skosHtmlManager.getApplicationRoot(SkosHtmlManager.askosiDisplayApplication) + "/skos.jsp?about=" + this.getAbout();
	}

	@XmlTransient
	public Namespace getRioNamespace() {
		if (namespace == null) return null;
		return new NamespaceImpl(this.getAbout(),namespace);
	}

	public void setRioNamespace(Namespace rioNamespace) {
		setNamespace(rioNamespace.getName());
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getDisplay(String language) {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getUrl(language);
	}
	public String getDisplayAdapted(SkosHtmlManager skosHtmlManager, Concept aConcept) {
		return skosHtmlManager.getAdaptedUrl(aConcept, conceptRepresentation.getUrl());
	}

	/**
	 * @param language
	 * @return the best URL for current language
	 */
	public String getCreate(String language) {
		return creates.getLing(language);
	}
	public String getCreateAdapted(SkosHtmlManager skosHtmlManager) {
		return skosHtmlManager.getAdaptedUrl(this, creates);
	}

	/**
	 * @param language:
	 *            language of need help
	 * @return help URL in desired language
	 */
	public String getHelp(String language) {
		return helps.getLing(language);
	}
	public String getHelpAdapted(SkosHtmlManager skosHtmlManager) {
		return skosHtmlManager.getAdaptedUrl(this,helps);
	}


	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getIcon(String language) {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getIcon(language);
	}
	public String getIconAdapted(SkosHtmlManager skosHtmlManager) {
		if (conceptRepresentation == null) return null;
		return skosHtmlManager.getAdaptedUrl(this, conceptRepresentation.getIcon());
	}

	/**
	 * @param language:
	 *            language of need help
	 * @return help URL in desired language
	 */
	public String getTitle(String language) {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getPrefLabel(language, false);
	}

	/**
	 * @return the noConcepts
	 */
	@XmlTransient
	public boolean isNoConcepts() {
		return noConcepts;
	}

	/**
	 * @param noConcepts the noConcepts to set
	 */
	public void setNoConcepts(boolean noConcepts) {
		this.noConcepts = noConcepts;
	}

	public String getDescription(String language) {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getScopeNote(language);
	}

	public String getRights(String language) {
		if (conceptRepresentation == null) return null;
		return conceptRepresentation.getEditorialNote(language);
	}

	/**
	 * @param entry
	 *            to put amongst concepts
	 */
	public synchronized Concept putConcept(Concept entry) {
		return entry;
	}

	/**
	 * @param help
	 *            the help to set
	 */
	public void putHelp(String help, String language) {
		this.helps.setLing(language, help);
	}

	public void putDisplay(String display, String language) {
		this.conceptRepresentation.putUrl(language, display);
	}

	public void putCreate(String create, String language) {
		this.creates.setLing(language, create);
	}

	public void putIcon(String icon, String language) {
		this.conceptRepresentation.putIcon(language, icon);
	}

	public void putTitle(String name, TermFormat format, String language, Status status) {
		this.conceptRepresentation.putPrefLabel(language, format, name, null, status);
	}

	public void putDescription(String name, TermFormat format, String language, Status status) {
		this.conceptRepresentation.putScopeNote(language, format,name, status);
	}

	public void putRights(String name, TermFormat format, String language, Status status) {
		this.conceptRepresentation.putEditorialNote(language, format,name, status);
	}

	/**
	 * @param code
	 *            the code to set
	 */
	public void setAbout(String code) {
		this.conceptRepresentation.setAbout(code);
	}

	/**
	 * @param conceptMap
	 *            the concepts to set
	 */
	public synchronized void setConcept(List<Concept> someEntries) {
	}

	/**
	 * @param displays the displays to set
	 */
	public void setDisplay(UrlList someDisplays) {
		this.conceptRepresentation.setUrl(someDisplays);
	}

	/**
	 * @param displays the displays to set
	 */
	public void setCreate(UrlList someCreates) {
		this.creates = someCreates;
	}

	/**
	 * @param internalNote the internal Note to set
	 */
	public void setInternalNote(String note) {
		this.conceptRepresentation.setInternalNote(note);
	}

	/**
	 * @param helps
	 *            the helps to set
	 */
	public void setHelp(UrlList helps) {
		this.helps = helps;
	}

	/**
	 * @param icons the icons to set
	 */
	public void setIcon(UrlList icons) {
		this.conceptRepresentation.setIcon(icons);
	}

    /**
     * Set the schema namespace.
     *
     * @param namespace  XML namespace URI
     */
	public void setNamespace(String namespace)
	{
		this.namespace = namespace;
	}

    /**
     * Set the ConceptScheme identification URL (URI)
     *
     * @param uri
     */
	public void setUri(String uri) {
		this.uri = uri;
	}


	/**
	 * @param names
	 *            the names to set
	 */
	public void setTitle(TermList names) {
		this.conceptRepresentation.setPrefLabel(names);
	}

	public void setDescription(TermList descripts) {
		this.conceptRepresentation.setScopeNote(descripts);
	}

    /*
     * How many concepts in this scheme?
     */
	@XmlTransient
	public int getSize() {
		return 0;
	}

	public ArrayList<SearchResult> labelMatch(String prefix, String userLanguage, boolean strictLanguage, LabelMatchType matchType) {
		return null;
	}

	public ArrayList<SearchResult> labelMatch(String prefix, String userLanguage, boolean strictLanguage, LabelMatchType matchType, Collection<Concept> filter) {
		return null;
	}


	/*
     * Application of this scheme
     */
	@XmlAttribute
	public String getApplication() {
		return this.application; 
	}

	/*
     * Application of this scheme
     */
	public void setApplication(String application) {
		if (application == null) this.application = null;
		else this.application = application.trim(); 
	}

	/*
	 * Indicates that a Scheme initial load is finished
	 */
	public void preLoadFinished() {
	}
	
	/*
	 * getNotationConcept is an helper for NotationScheme when the primary Scheme knows better how to find a given concept.
	 * Used primarily by DynamicScheme (all others are happy with the implementation below)
	 */
	public Concept getNotationConcept(String notationScheme, String handle) {
		return null;
	}

	public int compareTo(SchemeInterface o) {
		return getAbout().compareTo(o.getAbout());
	}
	
	@XmlTransient
	public boolean isDynamic() {
		return false;
	}

	/**
	 * @return the managerInstance
	 */
	@XmlTransient
	public SkosManager getManagerInstance() {
		return managerInstance;
	}

	/**
	 * @param managerInstance the managerInstance to set
	 */
	public void setManagerInstance(SkosManager managerInstance) {
		this.managerInstance = managerInstance;
		if (conceptRepresentation.getInScheme() == null) {
			conceptRepresentation.setInScheme(managerInstance.getMetaScheme());
		}
		this.managerInstance.putScheme(this);
	}

	/**
	 * @return the errors
	 */
	@XmlTransient
	public LocalError getErrors() {
		return this.conceptRepresentation.getErrors();
	}

	/**
	 * @param errors the errors to set
	 */
	public void setErrors(LocalError errors) {
		this.conceptRepresentation.setErrors(errors);
	}

	public String toTrace() {
		String aName = "no names";
		if (conceptRepresentation != null) {
			TermList labels = conceptRepresentation.getPrefLabel();
			if (labels != null) {
				aName = labels.getLingTerm(this.getSupraLanguage(),TermList.anyLanguage, false).toString();
				if (aName != null)
					aName = aName + " x " + labels.size();
				else aName = "empty";
			}
			else aName = "empty";
		}
		return "[" + this.getAbout() + "=" + aName + "]";
	}

	/**
	 * @return the lastLoaded
	 */
	@XmlTransient
	public Date getLastLoaded() {
		return lastLoaded;
	}

	public String toStringLastLoaded() {
		return formatter.format(lastLoaded);
	}

	/**
	 * @param lastLoaded the lastLoaded to set
	 */
	public void setLastLoaded(Date lastLoaded) {
		if (lastLoaded == null) this.lastLoaded = new Date();
		else this.lastLoaded = lastLoaded;
	}

	/**
	 * @return the supraLanguage
	 */
	@XmlElement
	public LinkedList<String> getSupraLanguage() {
		if (this.supraLanguage == null) this.supraLanguage = new LinkedList<String>();
		return this.supraLanguage;
	}

	/**
	 * @param supraLanguage the supraLanguage to set
	 */
	public void setSupraLanguage(LinkedList<String> supraLanguages) {
		this.supraLanguage = SkosUtil.cleanList(supraLanguages);
		log.debug(this.getAbout()+", supraLanguages="+supraLanguages+", cleaned="+this.supraLanguage);
	}

	@XmlTransient
	public boolean isHierarchized() {
		return this.hierarchized;
	}

	@XmlElement
	public LinkedList<String> getCreator() {
		return creators;
	}

	public void setCreator(LinkedList<String> creators) {
		this.creators = creators;
	}

	public void addCreator(String creator) {
		if (creator == null || creator.isEmpty()) return;
		if (this.creators == null) this.creators = new LinkedList<String>();
		if (!this.creators.contains(creator)) this.creators.addLast(creator);
	}

	@XmlElement
	public LinkedList<String> getContributor() {
		return contributors;
	}

	public void setContributor(LinkedList<String> contributors) {
		this.contributors = contributors;
	}

	public void addContributor(String contributor) {
		if (contributor == null || contributor.isEmpty()) return;
		if (this.contributors == null) this.contributors = new LinkedList<String>();
		if (!this.contributors.contains(contributor)) this.contributors.addLast(contributor);
	}

	public String toString() {
		return SESAME_NAMESPACE+getAbout();
	}

	public String stringValue() {
		return SESAME_NAMESPACE+getAbout();
	}

	@XmlTransient
	public String getNamespaceSesame() {
		return SESAME_NAMESPACE;
	}

	@XmlTransient
	public String getLocalName() {
		return getAbout();
	}
	
	public boolean equals(Object o) {
		if (o == null) return false;
		if (this == o) return true;

		if ( ! (o instanceof ConceptScheme)) return false;
		return this.getAbout().equals(((ConceptScheme)o).getAbout());
	}

	public int hashCode() {
		String identifier = this.getAbout();
		if (identifier == null) return 0;
		return identifier.hashCode();
	}

	@XmlTransient
	public Concept getConceptRepresentation() {
		return conceptRepresentation;
	}

	public void setConceptRepresentation(Concept conceptRepresentation) {
		this.conceptRepresentation = conceptRepresentation;
	}
	
	public LinkedList<Status> getPossibleStatus(Profile userProfile, Status currentStatus, Status[][] normalTransitions) {
		LinkedList<Status> result = new LinkedList<Status>();

		Status[] normalTransitionsRow = normalTransitions[currentStatus.ordinal()+1/*take into account initial "limbo" status*/];
		if (normalTransitionsRow.length > 0) {
			Status aStatus = normalTransitionsRow[0];
			Power neededPower = Status.powerNeeded[aStatus.ordinal()];
			if (userProfile.hasPower(neededPower,this.getAbout())) {
				result.add(aStatus); // Never include the currentStatus
				for (int i=1; i < normalTransitionsRow.length; i++) {
					result.add(aStatus); // Never include the currentStatus
				}
			}
		}
		result.add(currentStatus); // Act as a marker of the end of normal transitions!

		for (Status anotherStatus : Status.values()) {
			if (!result.contains(anotherStatus)) {
				Power neededPower = Status.powerNeeded[anotherStatus.ordinal()];
				if (userProfile.hasPower(neededPower,this.getAbout())) {
					result.add(anotherStatus);
				}
			}
		}
		return result;
	}

	private static final Runtime runtime = Runtime.getRuntime();

	@XmlTransient
	public long getLoadTime() {
		return loadTime;
	}
	public void setLoadTime(boolean finished) {
		if (finished) {
			loadTime = System.currentTimeMillis() - loadTime;
		} else {
			loadTime = System.currentTimeMillis();
		}
	}

	@XmlTransient
	public long getMemorySpace() {
		return memorySpace;
	}
	public void setMemorySpace(boolean finished) {
		
		runtime.runFinalization();
		runtime.gc();
		Thread.yield();

		MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
	    MemoryUsage memHeap = memoryMXBean.getHeapMemoryUsage();

		long bottom;
		long bottom2 = Long.MAX_VALUE;
		
		int i = 50;
		while ( (bottom = memHeap.getUsed()) < bottom2) {
			runtime.runFinalization();
			runtime.gc();
			Thread.yield();
			bottom2 = bottom;
			i--; // Protection over exagerate measures...
			if (i<=0) break;
		}

	    if (finished) {
			//memorySpace = runtime.totalMemory() - runtime.freeMemory() - memorySpace;
	    	memorySpace = bottom - memorySpace;
		} else {
			//memorySpace = runtime.totalMemory() - runtime.freeMemory();
			memorySpace = bottom;
		}
	}
	
	protected void complete(Concept aConcept) {
		// Nothing to do except for DynamicScheme...
	}

}
